﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace ExcelAutomator.Configuration
{
	[Serializable]
	[XmlType("excelAutomator", Namespace = Constants.Namespace)]
	[XmlRoot("excelAutomator", Namespace = Constants.Namespace, IsNullable = false)]
	public class ExcelAutomatorConfiguration
	{
		public ExcelAutomatorConfiguration()
		{
			ExecutionSets = new List<ExecutionSetConfiguration>();
		}


		[XmlElement("settings")]
		public SettingsConfiguration Settings { get; set; }
		
		[XmlElement("executionSet")]
		public List<ExecutionSetConfiguration> ExecutionSets { get; set; }



		#region FromXml

		public static ExcelAutomatorConfiguration FromXml(string xml)
		{
			return ConfigurationBuilder.Build<ExcelAutomatorConfiguration>(xml, GetDefaultSchemaContainer());
		}

		public static ExcelAutomatorConfiguration FromXml(string xml, IEnumerable<SchemaContainer> schemaExtensions)
		{
			var schemas = new List<SchemaContainer>(GetDefaultSchemaContainer());
			schemas.AddRange(schemaExtensions);
			return ConfigurationBuilder.Build<ExcelAutomatorConfiguration>(xml, schemas.DistinctBy(x=>x.SchemaXsd.GetHashCode()));
		}

		#endregion
		

		#region GetDefaultSchemaContainer

		private static IEnumerable<SchemaContainer> GetDefaultSchemaContainer()
		{
			var result = new List<SchemaContainer>
			             	{
			             		new SchemaContainer(Constants.Namespace, GetDefaultSchema()),
			             		new SchemaContainer(Constants.ExecutorExtensionsNamespace, GetExecutorsSchema())
			             	};

			return result;
		}

		#endregion

		#region GetDefaultSchema

		private static string GetDefaultSchema()
		{
			using (var schemaStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ExcelAutomator.Resources.Schema.xsd"))
			{
				if (schemaStream == null)
				{
					throw new InvalidOperationException("Couldn't extract the XML Schema from Assembly file.");
				}
				return new StreamReader(schemaStream).ReadToEnd();
			}
		}

		#endregion

		#region GetExecutorsSchema

		private static string GetExecutorsSchema()
		{
			using (var schemaStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ExcelAutomator.Resources.Executors.xsd"))
			{
				if (schemaStream == null)
				{
					throw new InvalidOperationException("Couldn't extract the XML Schema from Assembly file.");
				}
				return new StreamReader(schemaStream).ReadToEnd();
			}
		}

		#endregion


		#region GetXmlReaderSettings

		private static XmlReaderSettings GetXmlReaderSettings(IEnumerable<SchemaContainer> schemas)
		{
			var settings = new XmlReaderSettings
			               	{
			               		ConformanceLevel = ConformanceLevel.Document,
			               		CheckCharacters = true
			               	};
			settings.ValidationEventHandler += XmlValidationEventHandler;
			settings.ValidationType = ValidationType.Schema;
			settings.IgnoreWhitespace = true;
			settings.IgnoreComments = true;
			
			foreach (var schema in schemas)
			{
				settings.Schemas.Add(schema.Namespace, XmlReader.Create(new StringReader(schema.SchemaXsd)));
			}
			settings.Schemas.Compile();

			return settings;
		}

		#endregion


		private static void XmlValidationEventHandler(object sender, ValidationEventArgs e)
		{
			throw new XmlSchemaValidationException(e.Message, e.Exception);
		}

	}
}
